/*
 * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

#include "cds/aotLinkedClassBulkLoader.hpp"
#include "cds/aotMappedHeapLoader.hpp"
#include "cds/cdsConfig.hpp"
#include "cds/heapShared.inline.hpp"
#include "classfile/classLoader.hpp"
#include "classfile/classLoaderData.hpp"
#include "classfile/classLoaderDataShared.hpp"
#include "classfile/dictionary.hpp"
#include "classfile/javaClasses.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/vmClasses.hpp"
#include "classfile/vmSymbols.hpp"
#include "gc/shared/collectedHeap.hpp"
#include "jfr/jfrEvents.hpp"
#include "memory/metaspaceClosure.hpp"
#include "memory/universe.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/instanceRefKlass.hpp"
#include "oops/instanceStackChunkKlass.hpp"
#include "prims/jvmtiExport.hpp"
#include "runtime/globals.hpp"

InstanceKlass* vmClasses::_klasses[static_cast<int>(vmClassID::LIMIT)]
                                                 =  { nullptr /*, nullptr...*/ };
InstanceKlass* vmClasses::_box_klasses[T_VOID+1] =  { nullptr /*, nullptr...*/ };

bool vmClasses::is_loaded(InstanceKlass* klass) {
  return klass != nullptr && klass->is_loaded();
}

// Compact table of the vmSymbolIDs of all the VM classes (stored as short to save space)
static const short vm_class_name_ids[] = {
  #define VM_CLASS_NAME(name, symbol) ((short)VM_SYMBOL_ENUM_NAME(symbol)),
  VM_CLASSES_DO(VM_CLASS_NAME)
  #undef VM_CLASS_NAME
  0
};


#ifdef ASSERT
bool vmClasses::contain(Symbol* class_name) {
  int sid;
  for (int i = 0; (sid = vm_class_name_ids[i]) != 0; i++) {
    Symbol* symbol = vmSymbols::symbol_at(vmSymbols::as_SID(sid));
    if (class_name == symbol) {
      return true;
    }
  }
  return false;
}

bool vmClasses::contain(Klass* k) {
  return contain(k->name());
}
#endif

bool vmClasses::resolve(vmClassID id, TRAPS) {
  InstanceKlass** klassp = &_klasses[as_int(id)];

#if INCLUDE_CDS
  if (CDSConfig::is_using_archive() && !JvmtiExport::should_post_class_prepare()) {
    InstanceKlass* k = *klassp;
    assert(k->defined_by_boot_loader(), "must be");

    ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
    resolve_shared_class(k, loader_data, Handle(), CHECK_false);
    return true;
  }
#endif // INCLUDE_CDS

  if (!is_loaded(*klassp)) {
    int sid = vm_class_name_ids[as_int(id)];
    Symbol* symbol = vmSymbols::symbol_at(vmSymbols::as_SID(sid));
    Klass* k = SystemDictionary::resolve_or_fail(symbol, true, CHECK_false);
    (*klassp) = InstanceKlass::cast(k);
  }
  return ((*klassp) != nullptr);
}

void vmClasses::resolve_until(vmClassID limit_id, vmClassID &start_id, TRAPS) {
  assert((int)start_id <= (int)limit_id, "IDs are out of order!");
  for (auto id : EnumRange<vmClassID>{start_id, limit_id}) { // (inclusive start, exclusive end)
    if (CDSConfig::is_using_aot_linked_classes()) {
      precond(klass_at(id)->is_loaded());
    } else {
      resolve(id, CHECK);
    }
  }

  // move the starting value forward to the limit:
  start_id = limit_id;
}

void vmClasses::resolve_all(TRAPS) {
  assert(!Object_klass_is_loaded(), "well-known classes should only be initialized once");

  // Create the ModuleEntry for java.base.  This call needs to be done here,
  // after vmSymbols::initialize() is called but before any classes are pre-loaded.
  ClassLoader::classLoader_init2(THREAD);

#if INCLUDE_CDS
  if (CDSConfig::is_using_aot_linked_classes()) {
    AOTLinkedClassBulkLoader::preload_classes(THREAD);
  }
#endif

  // Preload commonly used klasses
  vmClassID scan = vmClassID::FIRST;
  // first do Object, then String, Class
  resolve_through(VM_CLASS_ID(Object_klass), scan, CHECK);
  CollectedHeap::set_filler_object_klass(vmClasses::Object_klass());
#if INCLUDE_CDS
  if (CDSConfig::is_using_archive()) {
#if INCLUDE_CDS_JAVA_HEAP
    if (HeapShared::is_loading() && HeapShared::is_loading_mapping_mode()) {
      // It's unsafe to access the archived heap regions before they
      // are fixed up, so we must do the fixup as early as possible
      // before the archived java objects are accessed by functions
      // such as java_lang_Class::restore_archived_mirror and
      // ConstantPool::restore_unshareable_info (restores the archived
      // resolved_references array object).
      //
      // AOTMappedHeapLoader::fixup_regions fills the empty
      // spaces in the archived heap regions and may use
      // vmClasses::Object_klass(), so we can do this only after
      // Object_klass is resolved. See the above resolve_through()
      // call. No mirror objects are accessed/restored in the above call.
      // Mirrors are restored after java.lang.Class is loaded.
      AOTMappedHeapLoader::fixup_region();
    }
    if (HeapShared::is_archived_heap_in_use() && !CDSConfig::is_using_full_module_graph()) {
      // Need to remove all the archived java.lang.Module objects from HeapShared::roots().
      ClassLoaderDataShared::clear_archived_oops();
    }
#endif // INCLUDE_CDS_JAVA_HEAP
    // Initialize the constant pool for the Object_class
    assert(Object_klass()->in_aot_cache(), "must be");
    Object_klass()->constants()->restore_unshareable_info(CHECK);
    resolve_through(VM_CLASS_ID(Class_klass), scan, CHECK);
  } else
#endif
  {
    resolve_through(VM_CLASS_ID(Class_klass), scan, CHECK);
  }

  assert(vmClasses::Object_klass() != nullptr, "well-known classes should now be initialized");

  java_lang_Object::register_natives(CHECK);

  // Calculate offsets for String and Class classes since they are loaded and
  // can be used after this point. These are no-op when CDS is enabled.
  java_lang_String::compute_offsets();
  java_lang_Class::compute_offsets();

  // Fixup mirrors for classes loaded before java.lang.Class.
  Universe::initialize_basic_type_mirrors(CHECK);
  Universe::fixup_mirrors(CHECK);

  if (CDSConfig::is_using_archive()) {
    // These should already have been initialized during CDS dump.
    assert(vmClasses::Reference_klass()->reference_type() == REF_NONE, "sanity");
    assert(vmClasses::SoftReference_klass()->reference_type() == REF_SOFT, "sanity");
    assert(vmClasses::WeakReference_klass()->reference_type() == REF_WEAK, "sanity");
    assert(vmClasses::FinalReference_klass()->reference_type() == REF_FINAL, "sanity");
    assert(vmClasses::PhantomReference_klass()->reference_type() == REF_PHANTOM, "sanity");
  } else {
    // If CDS is not enabled, the references classes must be initialized in
    // this order before the rest of the vmClasses can be resolved.
    resolve_through(VM_CLASS_ID(Reference_klass), scan, CHECK);

    // The offsets for jlr.Reference must be computed before
    // InstanceRefKlass::update_nonstatic_oop_maps is called. That function uses
    // the offsets to remove the referent and discovered fields from the oop maps,
    // as they are treated in a special way by the GC. Removing these oops from the
    // oop maps must be done before the usual subclasses of jlr.Reference are loaded.
    java_lang_ref_Reference::compute_offsets();

    // Preload ref klasses and set reference types
    InstanceRefKlass::update_nonstatic_oop_maps(vmClasses::Reference_klass());

    resolve_through(VM_CLASS_ID(PhantomReference_klass), scan, CHECK);
  }

  resolve_until(vmClassID::LIMIT, scan, CHECK);

  CollectedHeap::set_filler_object_klass(vmClasses::FillerObject_klass());

  _box_klasses[T_BOOLEAN] = vmClasses::Boolean_klass();
  _box_klasses[T_CHAR]    = vmClasses::Character_klass();
  _box_klasses[T_FLOAT]   = vmClasses::Float_klass();
  _box_klasses[T_DOUBLE]  = vmClasses::Double_klass();
  _box_klasses[T_BYTE]    = vmClasses::Byte_klass();
  _box_klasses[T_SHORT]   = vmClasses::Short_klass();
  _box_klasses[T_INT]     = vmClasses::Integer_klass();
  _box_klasses[T_LONG]    = vmClasses::Long_klass();

#ifdef ASSERT
  if (CDSConfig::is_using_archive()) {
    JVMTI_ONLY(assert(JvmtiExport::is_early_phase(),
                      "All well known classes must be resolved in JVMTI early phase"));
    for (auto id : EnumRange<vmClassID>{}) {
      InstanceKlass* k = _klasses[as_int(id)];
      assert(k->in_aot_cache(), "must not be replaced by JVMTI class file load hook");
    }
  }
#endif

  InstanceStackChunkKlass::init_offset_of_stack();
}

#if INCLUDE_CDS

void vmClasses::resolve_shared_class(InstanceKlass* klass, ClassLoaderData* loader_data, Handle domain, TRAPS) {
  assert(!Universe::is_fully_initialized(), "We can make short cuts only during VM initialization");
  assert(klass->in_aot_cache(), "Must be shared class");
  if (klass->class_loader_data() != nullptr) {
    return;
  }

  EventClassLoad class_load_event;

  // add super and interfaces first
  InstanceKlass* super = klass->super();
  if (super != nullptr && super->class_loader_data() == nullptr) {
    assert(super->is_instance_klass(), "Super should be instance klass");
    resolve_shared_class(super, loader_data, domain, CHECK);
  }

  Array<InstanceKlass*>* ifs = klass->local_interfaces();
  for (int i = 0; i < ifs->length(); i++) {
    InstanceKlass* ik = ifs->at(i);
    if (ik->class_loader_data()  == nullptr) {
      resolve_shared_class(ik, loader_data, domain, CHECK);
    }
  }

  klass->restore_unshareable_info(loader_data, domain, nullptr, THREAD);
  SystemDictionary::load_shared_class_misc(klass, loader_data);
  Dictionary* dictionary = loader_data->dictionary();
  dictionary->add_klass(THREAD, klass->name(), klass);
  klass->add_to_hierarchy(THREAD);
  assert(klass->is_loaded(), "Must be in at least loaded state");

  if (class_load_event.should_commit()) {
    JFR_ONLY(SystemDictionary::post_class_load_event(&class_load_event, klass, loader_data);)
  }
}

#endif // INCLUDE_CDS

// Tells if a given klass is a box (wrapper class, such as java.lang.Integer).
// If so, returns the basic type it holds.  If not, returns T_OBJECT.
BasicType vmClasses::box_klass_type(Klass* k) {
  assert(k != nullptr, "");
  for (int i = T_BOOLEAN; i < T_VOID+1; i++) {
    if (_box_klasses[i] == k)
      return (BasicType)i;
  }
  return T_OBJECT;
}
